//
//  ViewLayout.swift
//  EasyCoding
//
//  Created by Fanxx on 2019/5/30.
//  Copyright © 2019 Fanxx. All rights reserved.
//

import UIKit
import SnapKit

/// 布局参照对象
public enum ECViewLayoutReference {
    /// 默认前一个控件存在时为前一个，否则为父控件
    case `default`
    /// 父控件
    case parent
    /// 前一个控件
    case previous
    /// 后一个的控件
    case next
    /// 指定一个控件
    case specify(UIView)
    /// 没有指定对象，只针对自己布局
    case none
    /// 获取参照对象
    public func layoutReference(for view: UIView) -> UIView {
        switch self {
        case .default:
            let sp = view.superview!
            let idx = sp.subviews.firstIndex(of: view)!
            if idx == 0 {
                return sp
            }else{
                return sp.subviews[idx - 1]
            }
        case .parent: return view.superview!
        case .previous:
            let sp = view.superview!
            let idx = sp.subviews.firstIndex(of: view)!
            return sp.subviews[idx - 1]
        case .next:
            let sp = view.superview!
            let idx = sp.subviews.firstIndex(of: view)!
            return sp.subviews[idx + 1]
        case .specify(let target): return target
        case .none: return view
        }
    }
}

///约束值比较
public enum ECViewLayoutCompare {
    ///等于
    case equal
    ///小于等于
    case lessThanOrEqual
    ///大于等于
    case greatherThanOrEqual
}
extension ConstraintMakerExtendable {
    func compare(_ c: ECViewLayoutCompare, _ other: ConstraintRelatableTarget) -> ConstraintMakerEditable {
        switch c {
        case .equal:
            return self.equalTo(other)
        case .lessThanOrEqual:
            return self.lessThanOrEqualTo(other)
        case .greatherThanOrEqual:
            return self.greaterThanOrEqualTo(other)
        }
    }
}

///视图约束
public enum ECViewLayout {
    ///相对高度(比例，差额)
    case relativeHeight(CGFloat, CGFloat)
    ///相对宽度(比例，差额)
    case relativeWidth(CGFloat, CGFloat)
    
    ///自身高度
    case absoluteHeight(CGFloat)
    ///自身宽度
    case absoluteWidth(CGFloat)
    ///自身宽高比(比例，差额)
    case aspect(CGFloat, CGFloat)
    
    ///左-左
    case left(CGFloat)
    ///右-右
    case right(CGFloat)
    ///上-上
    case top(CGFloat)
    ///下-下
    case bottom(CGFloat)
    
    ///上基线
    case firstBaseline(CGFloat)
    ///线基线
    case lastBaseline(CGFloat)
    
    ///左-右
    case leftRight(CGFloat)
    ///右-左
    case rightLeft(CGFloat)
    ///上-下
    case topBottom(CGFloat)
    ///下-上
    case bottomTop(CGFloat)
    
    ///水平居中
    case centerX(CGFloat)
    ///竖直居中
    case centerY(CGFloat)
    
    ///小于等于
    indirect case less(ECViewLayout)
    ///大于等于
    indirect case greather(ECViewLayout)
    
    ///组合约束，用于自定义
    case set([ECViewLayout])
    ///自定义
    case custom(apply:(UIView, UIView)->Void)
    
    ///优先级
    indirect case priority(ECViewLayout, ConstraintPriority)
    ///更新约束
    indirect case update(ECViewLayout)
    
    ///指定参照对象
    indirect case refer(ECViewLayoutReference, ECViewLayout)
}

extension ConstraintMakerEditable {
    @discardableResult
    func ec_priority(_ value: ConstraintPriority?) -> ConstraintMakerFinalizable? {
        if let v = value {
            return self.priority(v)
        } else {
            return nil
        }
    }
}
extension ECViewLayout {
    ///若是负值需要取反
    func reverse(_ compare: ECViewLayoutCompare) -> ECViewLayoutCompare {
        switch compare {
        case .greatherThanOrEqual: return .lessThanOrEqual
        case .lessThanOrEqual: return .greatherThanOrEqual
        default: return compare
        }
    }
    ///应用约束
    public func apply(to target: UIView, refer ref: UIView, compare: ECViewLayoutCompare = .equal, priority: ConstraintPriority? = nil, isUpdating: Bool = false) {
        var abort = true
        switch self {
        case let .set(layout): layout.forEach({ $0.apply(to: target, refer: ref, compare: compare, priority: priority) })
        case let .custom(apply: apply): apply(target, ref)
        case let .less(layout):
            layout.apply(to: target, refer: ref, compare: .lessThanOrEqual, priority: priority, isUpdating: isUpdating)
        case let .greather(layout):
            layout.apply(to: target, refer: ref, compare: .greatherThanOrEqual, priority: priority, isUpdating: isUpdating)
        case let .priority(layout, value):
            layout.apply(to: target, refer: ref, compare: compare, priority: value, isUpdating: isUpdating)
        case let .update(layout):
            layout.apply(to: target, refer: ref, compare: compare, priority: priority, isUpdating: true)
        case let .refer(reference, layout):
            layout.apply(to: target, refer: reference.layoutReference(for: target), compare: compare, priority: priority, isUpdating: isUpdating)
        default: abort = false
        }
        if abort {
            return
        }
        let maker = isUpdating ? target.snp.updateConstraints : target.snp.makeConstraints
        maker { (make) in
            switch self {
            ///相对高度
            case let .relativeHeight(m, os):
                make.height.compare(compare, ref).offset(os).multipliedBy(m).ec_priority(priority)
            ///相对宽度
            case let .relativeWidth(m, os):
                make.width.compare(compare, ref).offset(os).multipliedBy(m).ec_priority(priority)
            ///绝对高度
            case let .absoluteHeight(v):
                make.height.compare(compare, v).ec_priority(priority)
            ///绝对宽度
            case let .absoluteWidth(v):
                make.width.compare(compare, v).ec_priority(priority)
            ///绝对宽度
            case let .aspect(m, os):
                make.width.compare(compare, target.snp.height).offset(os).multipliedBy(m).ec_priority(priority)
            ///左-左
            case let .left(v):
                make.left.compare(compare, ref).offset(v).ec_priority(priority)
            ///右-右
            case let .right(v):
                make.right.compare(reverse(compare), ref).offset(-v).ec_priority(priority)
            ///上-上
            case let .top(v):
                make.top.compare(compare, ref).offset(v).ec_priority(priority)
            ///下-下
            case let .bottom(v):
                make.bottom.compare(reverse(compare), ref).offset(-v).ec_priority(priority)
            
            ///上基线
            case let .firstBaseline(v): make.firstBaseline.compare(reverse(compare), ref).offset(-v).ec_priority(priority)
            ///下基线
            case let .lastBaseline(v): make.lastBaseline.compare(reverse(compare), ref).offset(-v).ec_priority(priority)
                
            ///左-右
            case let .leftRight(v): make.right.compare(reverse(compare), ref.snp.left).offset(-v).ec_priority(priority)
            ///右-左
            case let .rightLeft(v): make.left.compare(compare, ref.snp.right).offset(v).ec_priority(priority)
            ///上-下
            case let .topBottom(v): make.bottom.compare(reverse(compare), ref.snp.top).offset(-v).ec_priority(priority)
            ///下-上
            case let .bottomTop(v): make.top.compare(compare, ref.snp.bottom).offset(v).ec_priority(priority)
                
            ///水平居中
            case let .centerX(v): make.centerX.compare(compare, ref).offset(v).ec_priority(priority)
            ///竖直居中
            case let .centerY(v): make.centerY.compare(compare, ref).offset(v).ec_priority(priority)
            default: break
            }
        }
    }
    ///应用约束
    public func apply(to views: [UIView], refer: UIView) {
        views.forEach { self.apply(to: $0, refer: refer) }
    }
}
extension Array where Element == ECViewLayout {
    ///应用约束
    public func apply(to target: UIView, refer: UIView) {
        self.forEach { $0.apply(to: target, refer: refer) }
    }
    ///应用约束
    public func apply(to views: [UIView], refer: UIView) {
        self.forEach { $0.apply(to: views, refer: refer) }
    }
}

///快捷方式
extension ECViewLayout {
    ///相对高度
    public static var height: ECViewLayout { return .relativeHeight(1, 0) }
    ///相对宽度
    public static var width: ECViewLayout { return .relativeWidth(1, 0) }
    ///相对大小
    public static var size: ECViewLayout { return .set([.relativeWidth(1, 0), .relativeHeight(1, 0)]) }
    
    ///绝对高度
    public static func height(_ v:CGFloat) -> ECViewLayout {
        return .absoluteHeight(v)
    }
    ///绝对宽度
    public static func width(_ v:CGFloat) -> ECViewLayout {
        return .absoluteWidth(v)
    }
    ///绝对大小
    public static func size(_ widthHeight:CGFloat) -> ECViewLayout {
        return .set([.absoluteWidth(widthHeight), .absoluteHeight(widthHeight)])
    }
    ///绝对大小
    public static func size(_ width:CGFloat, _ height: CGFloat) -> ECViewLayout {
        return .set([.absoluteWidth(width), .absoluteHeight(height)])
    }
    ///宽高比
    public static func aspect(_ v:CGFloat) -> ECViewLayout {
        return .aspect(v, 0)
    }
    ///宽高相等
    public static var aspect: ECViewLayout { return .aspect(1, 0) }
    
    ///左左
    public static var left: ECViewLayout { return .left(0) }
    ///右右
    public static var right: ECViewLayout { return .right(0) }
    ///上上
    public static var top: ECViewLayout { return .top(0) }
    ///下下
    public static var bottom: ECViewLayout { return .bottom(0) }
    
    ///上基线对齐
    public static var firstBaseline: ECViewLayout { return .firstBaseline(0) }
    ///下基线对齐
    public static var lastBaseline: ECViewLayout { return .lastBaseline(0) }
    
    ///左右
    public static var leftRight: ECViewLayout { return .leftRight(0) }
    ///右左
    public static var rightLeft: ECViewLayout { return .rightLeft(0) }
    ///上下
    public static var topBottom: ECViewLayout { return .topBottom(0) }
    ///下上
    public static var bottomTop: ECViewLayout { return .bottomTop(0) }
    
    ///水平居中
    public static var centerX: ECViewLayout { return .centerX(0) }
    ///竖直居中
    public static var centerY: ECViewLayout { return .centerY(0) }
    
    ///居中
    public static func center(_ x: CGFloat,_ y:CGFloat) -> ECViewLayout {
        return .set([.centerX(x), .centerY(y)])
    }
    ///居中
    public static var center: ECViewLayout { return .center(0,0) }
    ///居中
    public static func center(_ v:CGPoint) -> ECViewLayout { return .center(v.x, v.y) }
    
    ///左-左，右-右
    public static func marginX(_ left: CGFloat,_ right: CGFloat) -> ECViewLayout {
        return .set([.left(left), .right(right)])
    }
    ///上-上，下-下
    public static func marginY(_ top: CGFloat,_ bottom: CGFloat) -> ECViewLayout {
        return .set([.top(top), .bottom(bottom)])
    }
    ///上-上，左-左，下-下，右-右
    public static func margin(_ top: CGFloat,_ left: CGFloat,_ bottom: CGFloat,_ right: CGFloat) -> ECViewLayout {
        return .set([.left(left), .right(right), .top(left), .bottom(right)])
    }
    ///水平边距
    public static func marginX(_ v:CGFloat) -> ECViewLayout { return .marginX(v, v) }
    ///竖直边距
    public static func marginY(_ v:CGFloat) -> ECViewLayout { return .marginY(v, v) }
    ///水平边距
    public static var marginX: ECViewLayout { return .marginX(0,0) }
    ///竖直边距
    public static var marginY: ECViewLayout { return .marginY(0,0) }
    
    ///边距(水平，竖直)
    public static func margin(_ vx:CGFloat, _ vy:CGFloat) -> ECViewLayout { return .margin(vy, vx, vy, vx) }
    ///边距
    public static func margin(_ v:CGFloat) -> ECViewLayout { return .margin(v, v, v, v) }
    ///四周贴边
    public static var margin: ECViewLayout { return .margin(0,0,0,0) }
    ///边距
    public static func margin(_ edge:UIEdgeInsets) -> ECViewLayout { return .margin(edge.top, edge.left, edge.bottom, edge.right) }
    
    ///优先级
    public func priority(_ value: ConstraintPriority) -> ECViewLayout {
        return .priority(self, value)
    }
    ///更新约束
    public var update: ECViewLayout {
        return .update(self)
    }
    
    ///指定页面布局
    public static func `for`(_ refer:ECViewLayoutReference, _ layouts: ECViewLayout...) -> ECViewLayout {
        return .refer(refer, .set(layouts))
    }
    ///指定页面布局
    public static func `for`(_ v:UIView, _ layouts: ECViewLayout...) -> ECViewLayout {
        return .refer(.specify(v), .set(layouts))
    }
    ///指定页面布局
    public func `for`(_ refer: ECViewLayoutReference) -> ECViewLayout {
        return .refer(refer, self)
    }
    ///指定页面布局
    public func `for`(_ view: UIView) -> ECViewLayout {
        return .for(view, self)
    }
}
